using DG.Tweening;
using System;
using UnityEngine;

namespace BToolkit
{
    [AddComponentMenu("BToolkit/LuckyWheelDriver")]
    [RequireComponent(typeof(CircleLayoutGroup))]
    [RequireComponent(typeof(CButton))]
    public class LuckyWheelDriver : MonoBehaviour
    {
        public float accelDuration = 1;
        public int decelRounds = 6;
        public float decelDuration = 6;
        public bool reverse;
        public bool freeRotate = true;
        public RectTransform rectTransform { get { return transform as RectTransform; } }
        public bool isRunning { get; private set; }

        private Vector2 screenUISize, center;
        private bool isDraging;
        private float previousAngle, dragDelta;
        private float counterPreviousAngle, onOverAngleCounter, setRunningFalseTimer;
        private Vector3 transAngle;
        private int tweenId;
        private float onOverAngleTargetAngle;
        private Action onOverAngleFunc;

        private void Awake()
        {
            transAngle = transform.localEulerAngles;
            tweenId = gameObject.GetInstanceID();

            CButton button = GetComponent<CButton>();
            button.triggerInterval = 0;
            button.globalSound = false;
            button.transition = CButton.Transition.None;
            button.AddDown(() =>
            {
                if (freeRotate && !this.isRunning)
                {
                    if (center == Vector2.zero) center = RootPos();
                    Vector2 downPos = ScreenToUGUIPoint(Input.mousePosition);
                    previousAngle = Mathf.Atan2(downPos.y - center.y, downPos.x - center.x) * Mathf.Rad2Deg;
                    isDraging = true;
                    dragDelta = 0;
                }
            });
        }

        private void Update()
        {
            if (!this.isRunning)
            {
                //自由转动
                if (isDraging)
                {
                    Vector2 downPos = ScreenToUGUIPoint(Input.mousePosition);
                    float angle = Mathf.Atan2(downPos.y - center.y, downPos.x - center.x) * Mathf.Rad2Deg;
                    dragDelta = angle - previousAngle;
                    previousAngle = angle;
                    transAngle.z = transAngle.z + dragDelta;
                    transform.localEulerAngles = transAngle;
                    if (Input.GetMouseButtonUp(0))
                    {
                        isDraging = false;
                    }
                }
                else
                {
                    dragDelta += (0 - dragDelta) * 0.05f;
                    transAngle.z = transform.localEulerAngles.z;
                    transAngle.z = transAngle.z + dragDelta;
                    transform.localEulerAngles = transAngle;
                }
            }

            if (setRunningFalseTimer > 0 && setRunningFalseTimer < Time.time)
            {
                setRunningFalseTimer = 0;
                isRunning = false;
            }
        }

        private Vector2 ScreenToUGUIPoint(Vector3 screenPos)
        {
            if (screenUISize == Vector2.zero)
            {
                Canvas[] canvases = GetComponentsInParent<Canvas>();
                for (int i = 0; i < canvases.Length; i++)
                {
                    Canvas canvas = canvases[i];
                    if (!canvas.overrideSorting)
                    {
                        screenUISize = (canvas.transform as RectTransform).sizeDelta;
                        break;
                    }
                }
            }
            Vector2 p;
            p.x = screenUISize.x * (screenPos.x - Screen.width * 0.5f) / (float)Screen.width;
            p.y = screenUISize.y * (screenPos.y - Screen.height * 0.5f) / (float)Screen.height;
            return p;
        }

        private Vector2 RootPos()
        {
            Vector2 pos = rectTransform.anchoredPosition;
            RectTransform parent = rectTransform.parent as RectTransform;
            Canvas _canvas = parent.GetComponent<Canvas>();
            while (!_canvas || _canvas.overrideSorting)
            {
                pos += parent.anchoredPosition;
                parent = parent.parent as RectTransform;
                _canvas = parent.GetComponent<Canvas>();
            }
            return pos;
        }

        private float Angle360(float angle)
        {
            while (angle < 0)
            {
                angle = angle + 360;
            }
            while (angle > 360)
            {
                angle = angle - 360;
            }
            return angle;
        }

        private void ListenAddAngle()
        {
            if (onOverAngleFunc != null)
            {
                float currAngle = Angle360(transform.localEulerAngles.z);
                float addValue = counterPreviousAngle - currAngle;
                counterPreviousAngle = currAngle;
                this.onOverAngleCounter = this.onOverAngleCounter + Mathf.Abs(addValue);
                if (this.onOverAngleCounter >= this.onOverAngleTargetAngle)
                {
                    this.onOverAngleCounter = 0;
                    this.onOverAngleFunc();
                }
            }
        }

        private void OnDestroy()
        {
            DOTween.Kill(tweenId);
        }


        #region 对外方法///////////////////////////////////////////////////////////////////////


        //初始化到原始角度
        public void InitAngle()
        {
            transAngle.z = 0;
            transform.localEulerAngles = transAngle;
        }
        /// <summary>
        /// 开始旋转
        /// </summary>
        /// <param name="stopAngle">停止在规定的角度</param>
        /// <param name="onStop">完全停止时的回调</param>
        public void StartRun(float stopAngle, Action onStop)
        {
            if (isRunning)
            {
                return;
            }
            isDraging = false;
            dragDelta = 0;
            counterPreviousAngle = transform.localEulerAngles.z;
            onOverAngleCounter = 0;
            isRunning = true;
            float _stopAngle = stopAngle - decelRounds * 360;
            if (reverse)
            {
                _stopAngle = stopAngle + decelRounds * 360;
            }
            //加速一圈
            float targetAngle = reverse ? transform.eulerAngles.z + 360 : transform.eulerAngles.z - 360;
            DOVirtual.Float(transform.eulerAngles.z, targetAngle, accelDuration, (float angle) =>
            {
                transform.eulerAngles = new Vector3(0, 0, angle);
            }).SetEase(Ease.InSine).SetId(tweenId).OnComplete(() =>
            {
                //开始减速
                transform.DORotate(new Vector3(0, 0, _stopAngle), decelDuration, RotateMode.FastBeyond360)
                    .SetId(tweenId)
                    .OnUpdate(ListenAddAngle)
                    .OnComplete(() =>
                    {
                        onStop?.Invoke();
                        setRunningFalseTimer = Time.time + 1f;
                    });
            });
        }

        /// <summary>
        /// 指定一个度数，每次转满这个度数会回调一次。可用于播放转盘飞轮弹簧“哒哒”的声音
        /// </summary>
        public void SetOverAngleCallback(float subAngle, Action callbackFunc)
        {
            this.onOverAngleTargetAngle = subAngle;
            this.onOverAngleFunc = callbackFunc;
        }

        /// <summary>
        /// 不等转完，立即结束并停在特定的角度
        /// </summary>
        public void StopAtAngle(float stopAngle)
        {
            DOTween.Kill(tweenId);
            this.isRunning = false;
            this.dragDelta = 0;
            this.transAngle.z = stopAngle;
            this.transform.localEulerAngles = transAngle;
        }

        #endregion

    }
}